home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-04-30 | 20.5 KB | 931 lines | [TEXT/MPS ] |
- /*
- File: SerialSample.cp
-
- Contains: Sample program for a Serial endpoint
-
- Copyright: © 1992-1997 by Apple Computer, Inc., all rights reserved.
-
- */
-
- //
- // We want to use the OT 'new' operator, not the standard MPW one, so include
- // OpenTptGlobalNew.h first.
- //
- #ifndef __OPENTPTGLOBALNEW__
- #include <OpenTptGlobalNew.h>
- #endif
- #ifndef __OPENTPTSERIAL__
- #include <OpenTptSerial.h>
- #endif
-
- #ifndef __STDIO__
- #include <stdio.h>
- #endif
- #ifndef __EVENTS__
- #include <Events.h>
- #endif
- #ifndef __MENUS__
- #include <Menus.h>
- #endif
- #ifndef __DEVICES__
- #include <Devices.h>
- #endif
- #ifndef __STRING__
- #include <String.h>
- #endif
-
- /*******************************************************************************
- ** GlobalVariables
- ********************************************************************************/
-
- struct StateInfo
- {
- TEndpoint* endPt;
- long sysTaskRef;
- OSStatus error;
- Boolean lastWasCR;
- UInt8 prevCR;
- Boolean ready;
- Boolean unbound;
- Boolean disconnected;
- Boolean connect;
- Boolean scheduled;
- Boolean optMgmtDone;
- Boolean ioctlDone;
- Boolean acceptDone;
- Boolean passconDone;
- };
-
- /*******************************************************************************
- ** SerialProcessProc
- ********************************************************************************/
-
- pascal void SerialProcessProc(void* contextPtr)
- {
- StateInfo* info = (StateInfo*)contextPtr;
- OTResult result;
- UInt8 buf[256];
-
- info->scheduled = false;
- while ( true )
- {
- OTFlags flags;
- result = OTRcv(info->endPt, buf, 256, &flags);
- if ( result < 0 )
- {
- if ( result != kOTNoDataErr )
- DebugStr("\pError in Rcv");
- break;
- }
- else
- {
- OTResult idx;
- //
- // The wierdness with 0x0a and 0x0d is to try to be system-independent
- // of how CR/LF pairs happen. If either character is received, if the
- // previous character was not an 0x0a or 0x0d, then we output the CR/LF,
- // and flag that the last was a CR, and remember which it was.
- // If the previous CR/LF character was the same one as our current
- // character, then this is a new CR/LF. The jist of all of this is
- // that if we get an 0x0a or 0x0d, if the next character is the
- // other one, we will ignore it.
- //
- for ( idx = 0; idx < result; ++idx )
- {
- UInt8 c = buf[idx];
- if ( c >= 0x20 && c <= 0x7f )
- {
- info->lastWasCR = false;
- printf("%c", c);
- }
- else if ( c == 0x0a || c == 0x0d )
- {
- if ( !info->lastWasCR || info->prevCR == c )
- {
- info->lastWasCR = true;
- info->prevCR = c;
- printf("\n");
- }
- else
- info->lastWasCR = false;
- }
- else
- {
- info->lastWasCR = false;
- printf("\\x%02X", c);
- }
- }
- fflush(stdout);
- }
- }
- }
-
- /*******************************************************************************
- ** Event handler...
- ********************************************************************************/
-
- pascal void SerialEventHandler(void* contextPtr, OTEventCode code,
- OTResult result, void* /* cookie */)
- {
- register StateInfo* info = (StateInfo*)contextPtr;
-
- switch ( code )
- {
- //
- // We could poll for the data in our simple example here, but if we were doing
- // more complex things than just scanning the keyboard for keys, that would
- // be inconvient. By using the OTScheduleSystemTask, we can read our characters
- // at SystemTask time, and then output them however we want to.
- //
- case T_DATA:
- {
- OTScheduleSystemTask(info->sysTaskRef);
- break;
- }
-
- case T_GODATA:
- break;
- //
- // Our Bind is completing - if it succeeded, issue a Connect() if we are supposed
- // to be connecting. Otherwise, just sit tight until we get a T_LISTEN event that
- // indicates someone is trying to talk to us on the serial port.
- //
- case T_BINDCOMPLETE:
- {
- if ( result != kOTNoError )
- {
- DebugStr("\pAsync Bind failed.");
- info->error = (OSStatus)result;
- }
- else
- info->unbound = false;
-
- if ( info->connect )
- {
- TCall req;
-
- req.addr.buf = NULL;
- req.addr.len = 0;
- req.opt.buf = NULL;
- req.opt.len = 0;
- req.udata.buf = NULL;
- req.udata.len = 0;
-
- info->error = OTConnect(info->endPt, &req, NULL);
- }
- if ( info->error != kOTNoError )
- info->ready = true;
- break;
- }
- //
- // Unbind is complete
- //
- case T_UNBINDCOMPLETE:
- {
- if ( result != 0 )
- DebugStr("\pAsync Bind failed.");
- info->error = (OSErr)result;
- info->unbound = true;
- break;
- }
-
- case kStreamIoctlEvent:
- {
- if ( result != 0 )
- DebugStr("\pIOCTL call failed.");
- info->ioctlDone = true;
- break;
- }
- //
- // An Option Management call is complete
- //
- case T_OPTMGMTCOMPLETE:
- {
- if ( result != 0 )
- DebugStr("\pOption Management call failed.");
- info->optMgmtDone = true;
- break;
- }
- //
- // a SndDisconnect has completed. If info->error is not kOTNoError, then we
- // did the disconnect because of an error, so let's not overwrite the error.
- // Then, let's do an unbind.
- //
- case T_DISCONNECTCOMPLETE:
- {
- if ( result != 0 )
- DebugStr("\pAsync disconnect failed.");
- if ( info->error == kOTNoError )
- info->error = (OSStatus)result;
- info->disconnected = true;
- OTUnbind(info->endPt);
- break;
- }
- //
- // Our connect request is complete, do a RcvConnect if all is well.
- //
- case T_CONNECT:
- {
- if ( result != 0 )
- DebugStr("\pAsync connect failed.");
- info->error = (OSErr)result;
-
- if ( info->error == kOTNoError )
- {
- TCall retCall;
-
- retCall.addr.buf = NULL;
- retCall.addr.maxlen = 0;
- retCall.opt.buf = NULL;
- retCall.opt.maxlen = 0;
- retCall.udata.buf = NULL;
- retCall.udata.maxlen= 0;
-
- info->error = OTRcvConnect(info->endPt, &retCall);
- }
- info->ready = true;
- break;
- }
- //
- // Our Accept is complete - we're ready for action!
- //
- case T_ACCEPTCOMPLETE:
- {
- info->acceptDone = true;
- if ( result != kOTNoError )
- {
- DebugStr("\pAsync accept failed.");
- info->error = (OSErr)result;
- info->ready = true;
- }
- else if ( info->passconDone )
- {
- info->error = kOTNoError;
- info->ready = true;
- }
- break;
- }
- //
- // Our endpoint is now ready for action - we've got a connection
- //
- case T_PASSCON:
- {
- //
- // T_PASSCON's can't have an error
- //
- info->passconDone;
- if ( info->acceptDone )
- {
- info->error = kOTNoError;
- info->ready = true;
- }
- break;
- }
- //
- // We have an incoming connection request. We do a Listen to get the information
- // on the request (for Serial connections, this is really a formality, but...)
- // then issue an Accept() on the same endpoint.
- // If something goes wrong, we issue a SndDisconnect(), which has the effect of
- // rejecting the connection request.
- //
- case T_LISTEN:
- {
- TCall lCall;
-
- lCall.addr.buf = NULL;
- lCall.addr.maxlen = 0;
- lCall.opt.buf = NULL;
- lCall.opt.maxlen = 0;
- lCall.udata.buf = NULL;
- lCall.udata.maxlen = 0;
-
- info->error = OTListen(info->endPt, &lCall);
- if ( info->error != kOTNoError )
- info->ready = true;
- else
- {
- TCall* aCall = new TCall;
- if ( aCall == NULL )
- {
- info->error = kOTOutOfMemoryErr;
- info->ready = true;
- OTSndDisconnect(info->endPt, NULL);
- break;
- }
- aCall->addr.buf = NULL; // Accept doesn't look at or use the "addr" field
- aCall->addr.maxlen = 0;
- aCall->opt.buf = NULL;
- aCall->opt.len = 0;
- aCall->udata.buf = NULL;
- aCall->udata.len = 0;
- aCall->sequence = lCall.sequence;
- info->error = OTAccept(info->endPt, info->endPt, aCall);
- if ( info->error != kOTNoError )
- {
- delete aCall;
- info->ready = true;
- OTSndDisconnect(info->endPt, NULL);
- }
- }
- break;
- }
-
- default:
- DebugStr("\pUnknown or unexpected event");
- break;
- }
- }
-
- /*******************************************************************************
- ** ShowEndpointOptions
- ********************************************************************************/
-
- void ShowEndpointOptions(StateInfo* info, Boolean* isBreakOn)
- {
- OSStatus err;
- TOptMgmt* ret = (TOptMgmt*)OTAlloc(info->endPt, T_OPTMGMT, T_OPT, &err);
-
-
- printf("Current Settings for endpoint @ %08lX:\n", (long)info->endPt);
-
- do
- {
- if ( ret == NULL )
- {
- printf("ERROR: could not allocate TOptMgmt structure (%d)\n", err);
- fflush(stdout);
- break;
- }
- //
- // Get the current options
- //
- TOptMgmt req;
- TOptionHeader option;
-
- option.len = kOTOptionHeaderSize;
- option.level = COM_SERIAL;
- option.name = T_ALLOPT;
-
- req.opt.buf = (UInt8*)&option;
- req.opt.len = kOTOptionHeaderSize;
- req.flags = T_CURRENT;
-
- info->optMgmtDone = false;
- err = OTOptionManagement(info->endPt, &req, ret);
- if ( err != kOTNoError )
- {
- printf("ERROR: OptionManagement T_CURRENT request returned %d\n", err);
- break;
- }
- while ( !info->optMgmtDone )
- ;
- //
- // Now, let's print the options
- //
- {
- TOption* opt = (TOption*)ret->opt.buf;
- char string[512];
-
- err = OTCreateOptionString(kSerialName, &opt, ret->opt.buf + ret->opt.len,
- string, sizeof(string));
-
- if ( err == kOTNoError )
- {
- char* str = string;
- size_t len = 0;
- while ( true )
- {
- char* temp = strchr(str, ',');
- if ( temp == NULL )
- {
- printf("%s\n", str);
- break;
- }
- if ( len + temp - str + 1 > 60 )
- {
- printf("\n");
- if ( *str == ' ' )
- str += 1;
- len = 0;
- }
- printf("%*.*s", temp - str + 1, temp - str + 1, str);
- len += temp - str + 1;
- str = temp + 1;
- }
- fflush(stdout);
- }
- }
-
- TOption* opt = OTFindOption(ret->opt.buf, ret->opt.len, COM_SERIAL, SERIAL_OPT_STATUS);
-
- if ( opt != NULL )
- {
- if ( !(*(UInt32*)opt->value & kOTSerialOutputBreakOn) )
- *isBreakOn = false;
- else
- *isBreakOn = true;
- }
- else
- printf("ERROR:OptionManagement did not have SERIAL_OPT_STATUS in returned options\n");
- } while ( false );
- fflush(stdout);
- OTFree(ret, T_OPTMGMT);
- }
-
- /*******************************************************************************
- ** DoBreak
- ********************************************************************************/
-
- void DoBreak(StateInfo* info, Boolean* breakOn, Boolean timed)
- {
- UInt32 value;
- OSStatus err;
-
-
- if ( timed )
- value = 5000;
- else if ( *breakOn )
- value = kOTSerialSetBreakOff;
- else
- value = kOTSerialSetBreakOn;
-
- err = OTIoctl(info->endPt, I_SetSerialBreak, (void*)value);
-
- if ( err != kOTNoError )
- printf("ERROR: OptionManagement T_NEGOTIATE request returned %d\n", err);
- else
- {
- while ( !info->ioctlDone )
- ;
- if ( value == 0 )
- *breakOn = 0;
- else
- *breakOn = 1;
- }
- fflush(stdout);
- }
-
- /*******************************************************************************
- ** SetOptions
- ********************************************************************************/
-
- Boolean SetOptions(StateInfo* info, char* str)
- {
- TOptMgmt cmd;
- OSStatus err;
- UInt8 buf[512];
-
- cmd.opt.buf = buf;
- cmd.opt.len = 0;
- cmd.opt.maxlen = sizeof(buf);
- cmd.flags = T_NEGOTIATE;
-
- err = OTCreateOptions(kSerialName, &str, &cmd.opt);
-
- if ( err != kOTNoError )
- {
- printf("ERROR: OTCreateOptions returned %d\n", err);
- fflush(stdout);
- return false;
- }
- //
- // Make the option management call to set stuff up
- //
- info->optMgmtDone = false;
- err = OTOptionManagement(info->endPt, &cmd, &cmd);
- if ( err != kOTNoError )
- {
- printf("ERROR: OptionManagement returned %d\n", err);
- fflush(stdout);
- return false;
- }
- else
- {
- while ( !info->optMgmtDone )
- ;
- }
- return true;
- }
-
- /*******************************************************************************
- ** SetParameters
- ********************************************************************************/
-
- Boolean SetParameters(StateInfo* info)
- {
- UInt32 baud;
- UInt32 databits;
- UInt32 stopbits;
- UInt32 parity;
- char str1[4];
- char str2[4];
- char buf[256];
-
- printf("\nEnter parameters (BaudRate, [5 6 7 8 ], [N O E], [10 15 20]):\n");
- fflush(stdout);
- fflush(stdin);
- clearerr(stdin);
- if ( scanf("%lu , %1[5678], %1[NOEnoe], %lu", &baud, str1, str2, &stopbits) != 4 )
- {
- printf("ERROR: Bad format - aborted\n");
- fflush(stdout);
- fflush(stdin);
- return false;
- }
- databits = str1[0] - '0';
-
- if ( str2[0] == 'o' || str2[0] == 'O' )
- parity = kOTSerialOddParity;
- else if ( str2[0] == 'e' || str2[0] == 'E' )
- parity = kOTSerialEvenParity;
- else
- parity = kOTSerialNoParity;
-
- if ( stopbits != 10 && stopbits != 15 && stopbits != 20 )
- {
- printf("ERROR: Bad format - aborted\n");
- fflush(stdout);
- return false;
- }
- sprintf(buf, "BaudRate=%lu DataBits=%lu StopBits=%lu Parity=%lu",
- baud, databits, stopbits, parity);
-
- return SetOptions(info, buf);
- }
-
- /*******************************************************************************
- ** GetKey
- ********************************************************************************/
-
- #define kCmdE -2
- #define kCmdQ -3
- #define kCmdB -4
- #define kCmdI -5
- #define kCmdT -6
- #define kCmdP -7
-
- int GetKey()
- {
- EventRecord event;
-
- if ( GetNextEvent(keyDownMask | autoKeyMask, &event) )
- {
- char key = (char)(event.message & charCodeMask);
- if ( event.modifiers & cmdKey )
- {
- if ( key == '.' )
- return -1;
- if ( event.what != keyDown )
- return 0;
- if ( key == 'e' || key == 'E' )
- return kCmdE;
- if ( key == 'q' || key == 'Q' )
- return kCmdQ;
- if ( key == 'b' || key == 'B' )
- return kCmdB;
- if ( key == 't' || key == 'T' )
- return kCmdT;
- if ( key == 'i' || key == 'I' )
- return kCmdI;
- if ( key == 'p' || key == 'P' )
- return kCmdP;
- SysBeep(5);
- return 0;
- }
- if ( event.modifiers & optionKey )
- {
- SysBeep(5);
- return 0;
- }
- if ( event.modifiers & controlKey )
- {
- if ( key > 0 && key <= 0x1f )
- return key;
- SysBeep(5);
- return 0;
- }
- if ( key >= 0x01 && key <= 0x7f )
- return key;
- SysBeep(5);
- }
- return 0;
- }
-
- /*******************************************************************************
- ** DoTest
- ********************************************************************************/
-
- void DoTest()
- {
- StateInfo info;
- OSStatus err;
-
- OTMemset(&info, 0, sizeof(info));
-
- do
- {
- //
- // Create a task scheduler so that we can "fprintf" stuff from our
- // event handler.
- //
- info.sysTaskRef = OTCreateSystemTask(SerialProcessProc, &info);
- info.unbound = true;
-
- if ( info.sysTaskRef == 0 )
- {
- printf("ERROR: Could not create System Task\n");
- fflush(stdout);
- break;
- }
- //
- // Do a synchronous Open of the endpoint
- //
- info.endPt = OTOpenEndpoint(OTCreateConfiguration(kSerialName), 0, NULL, &err);
-
- if ( err != kOTNoError )
- {
- printf("ERROR: open of Serial Endpoint failed (%d).\n", err);
- fflush(stdout);
- break;
- }
-
- //
- // Install notifier we're going to use for testing. Pass the StateInfo
- // structure as the 'context' value that the event handler will get back.
- // (Note, can not use zero as the context value)
- //
- err = OTInstallNotifier(info.endPt, &SerialEventHandler, &info);
- if ( err != kOTNoError )
- {
- printf("ERROR: InstallNotifier() failed with %d\n", err);
- fflush(stdout);
- break;
- }
-
- OTSetAsynchronous(info.endPt); // Ensure endpoint is async
- OTSetNonBlocking(info.endPt); // Ensure endpoint is and non-blocking
-
- do
- {
- Boolean breakOn;
- ShowEndpointOptions(&info, &breakOn);
-
- //
- // Ask user what to do
- //
- int testNum = 0;
- Boolean echo = true;
-
- printf("\nChoose Test:\n");
- printf(" 1) Connect to remote node\n");
- printf(" 2) Listen and accept connection from remote node\n");
- printf(" 3) Set Parameters\n");
- printf(" 4) Quit\n");
- fflush(stdout);
-
- while ( true )
- {
- int c;
-
- switch ( c = GetKey() )
- {
- case 0:
- break;
-
- case -1:
- c = '4';
- /* fall thru */
-
- case '1':
- case '2':
- case '3':
- case '4':
- testNum = c - '0';
- printf("%c\n", c);
- fflush(stdout);
- break;
-
- default:
- SysBeep(5);
- break;
- }
- if ( testNum != 0 )
- break;
- }
-
- if ( testNum == 4)
- break;
- //
- // We'll start off the bind, and let the asynchronous event handler take it
- // from there.
- //
- info.acceptDone = false;
- info.passconDone= false;
- info.ready = false;
- info.error = kOTNoError;
- info.connect = testNum == 1;
- {
- TBind bind;
-
- bind.addr.buf = NULL;
- bind.addr.len = 0;
- bind.qlen = info.connect ? 0 : 1;
-
- err = OTBind(info.endPt, &bind, NULL);
- if ( err != kOTNoError )
- {
- printf("ERROR: Bind returned %d\n", err);
- fflush(stdout);
- break;
- }
- }
-
- if ( testNum == 3 )
- {
- SetParameters(&info);
- OTUnbind(info.endPt);
- continue;
- }
-
- Boolean aborted = false;
- if ( !info.connect )
- {
- printf("Waiting for incoming connection\n");
- printf("Press mouse button to abort\n");
- fflush(stdout);
- }
- while ( !info.ready )
- {
- if ( Button() )
- {
- while ( Button() )
- ;
- aborted = true;
- break;
- }
- }
- if ( info.error != kOTNoError )
- break;
-
- if ( !aborted )
- {
- if ( !info.connect )
- printf("Incoming connection received and accepted\n\n");
- printf("Command-. aborts Command-E toggles echo on and off\n");
- printf("Command-B toggles Break Command-T turns break on for 5 seconds\n");
- printf("Command-I shows endpoint options Command-P changes parameters\n");
- printf("=========================================================================\n\n");
- fflush(stdout);
- while ( true )
- {
- char buf[20];
- size_t idx = 0;
- int c;
-
- SystemTask();
- while ( (c = GetKey()) != 0 )
- {
- if ( c < 0 )
- {
- Boolean ok = true;
- switch ( c )
- {
- case kCmdE: echo = !echo; break;
- case kCmdB: DoBreak(&info, &breakOn, false); break;
- case kCmdT: DoBreak(&info, &breakOn, true); break;
- case kCmdI: ShowEndpointOptions(&info, &breakOn); break;
-
- case kCmdP:
- ShowEndpointOptions(&info, &breakOn);
- if ( SetParameters(&info) )
- ShowEndpointOptions(&info, &breakOn);
- break;
-
- case kCmdQ:
- default:
- ok = false;
- break;
- }
- if ( ok )
- continue;
- break;
- }
- if ( echo )
- {
- if ( c >= 0x20 && c <= 0x7f )
- printf("%c", c);
- else if ( c == 0x0a || c == 0x0d )
- printf("\n");
- fflush(stdout);
- }
- buf[idx++] = c;
- }
- if ( c < 0 )
- break;
-
- OTResult sent;
- char* toSend = buf;
-
- while ( true )
- {
- sent = OTSnd(info.endPt, toSend, idx, T_MORE);
- if ( sent < 0 && sent != kOTFlowErr )
- {
- printf("\n*****\n ERROR: Snd() return error %d\n*****\n", err);
- fflush(stdout);
- break;
- }
- if ( sent == idx )
- break;
- if ( sent > 0 )
- {
- toSend += sent;
- idx -= (size_t)sent;
- }
- }
- }
- }
- printf("Disconnecting endpoint\n");
- fflush(stdout);
- //
- // If SndDisconnect returns an error, it's probably because
- // we're listening and haven't gotten a connection yet.
- //
- if ( OTSndDisconnect(info.endPt, NULL) != kOTNoError )
- OTUnbind(info.endPt);
- while ( !info.unbound )
- ;
-
- } while ( true );
- } while ( false );
-
- //
- // Clean up the endpoint we allocated
- //
- if ( info.endPt != NULL )
- {
- OTResult state = OTGetEndpointState(info.endPt);
-
- if ( state == T_DATAXFER || state == T_INCON || state == T_OUTCON )
- {
- printf("Disconnecting endpoint\n");
- fflush(stdout);
- OTSndDisconnect(info.endPt, NULL);
- }
- else if ( state == T_IDLE )
- {
- printf("Unbinding endpoint\n");
- fflush(stdout);
- OTUnbind(info.endPt);
- }
- while ( !info.unbound )
- ;
- OTRemoveNotifier(info.endPt);
- OTSetSynchronous(info.endPt);
- OTSetBlocking(info.endPt);
- err = OTCloseProvider(info.endPt);
- if ( err != kOTNoError )
- {
- printf("ERROR: CloseEndpoint returned %d\n", err);
- fflush(stdout);
- }
- }
- OTDestroySystemTask(info.sysTaskRef);
- }
-
- /*******************************************************************************
- ** Initialize Open Transport and call DoTest function
- ********************************************************************************/
-
- main(int, char**)
- {
- InitGraf(&qd.thePort); // initialize quickdraw so we can use regions
-
- printf("SerialSample showing usage of Serial endpoints.\n\n");
- fflush(stdout);
- //
- // Initialize Open Transport
- //
- if ( InitOpenTransport() == kOTNoError )
- {
- //
- // Run the test
- //
- DoTest();
-
- //
- // Shut down Open Transport.
- // Not strictly necessary since it patches _ExitToShell and will
- // clean us up anyway.
- //
- CloseOpenTransport();
-
- printf("\n\nDone\n");
- fflush(stdout);
-
- return 0;
- }
- return 1;
- };
-
-